Skip to content

Insert file mentions when non-image files are dropped on the composer - #5390

Open
0xjohnnydev wants to merge 2 commits into
pingdotgg:mainfrom
0xjohnnydev:feat/drop-non-image-file-mentions
Open

Insert file mentions when non-image files are dropped on the composer#5390
0xjohnnydev wants to merge 2 commits into
pingdotgg:mainfrom
0xjohnnydev:feat/drop-non-image-file-mentions

Conversation

@0xjohnnydev

@0xjohnnydev 0xjohnnydev commented Aug 5, 2026

Copy link
Copy Markdown

Problem

Dropping a file onto the composer routes every dropped file through the image-attachment path (onComposerDropaddComposerImages). Anything that isn't an image is rejected with:

Unsupported file type for ''. Please attach image files only.

So there's no way to drag a file from Finder/Explorer to reference it by path — you can only do it by typing @ or dragging from the in-app file tree.

Change

onComposerDrop now partitions the dropped files:

  • Images → attach inline, exactly as before.
  • Other files → become composer file mentions, using the dropped File's on-disk path resolved via Electron webUtils.getPathForFile, exposed as a new optional DesktopBridge.getPathForFile.
    • Paths inside the workspace cwd are made workspace-relative so they match typed / file-tree mentions; paths outside stay absolute.
  • Files whose path can't be resolved (browser builds, in-memory Files) fall back to the existing image path, so the "unsupported file type" error still surfaces and non-desktop behavior is unchanged.

Files

  • packages/contracts/src/ipc.ts — add optional DesktopBridge.getPathForFile (typed unknown since contracts is built without the DOM lib).
  • apps/desktop/src/preload.ts — implement it via webUtils.getPathForFile (synchronous, in-process, no IPC hop).
  • apps/web/src/components/chat/composerFileDrop.tspartitionDroppedComposerFiles, toComposerMentionPath, buildDroppedFileMentions (pure helpers) + composerFileDrop.test.ts.
  • apps/web/src/components/chat/ChatComposer.tsx — wire helpers into onComposerDrop; insertComposerTextAtEnd moved above the drop handler so it's defined before use.

Notes / open questions

  • Absolute-vs-relative: in-workspace drops become relative to match existing mentions; out-of-workspace drops keep the absolute path so the reference stays unambiguous. Happy to change this if you'd rather always keep absolute (or reject out-of-workspace drops).
  • cwd prefix match is case-sensitive, so a drop differing only in case from the cwd (possible on case-insensitive volumes) keeps its absolute path rather than guessing.
  • Remote environments (reviewer finding, needs a maintainer call): getPathForFile resolves paths on the local desktop host, while mentions are resolved against the environment workspace. On a local environment (the common case) these agree. On SSH/WSL environments the inserted mention would be a local path the backend can't read (before this PR such drops errored loudly). Options: gate mention insertion on the environment being local, or accept the dangling reference. Happy to implement whichever you prefer.
  • Dropping a folder now inserts a folder mention instead of erroring (folders have empty MIME type, so they take the mention path).
  • Draft: opened for testing/review before it's ready to merge.

Note

Insert file-mention tokens when non-image files are dropped on the composer

  • Adds a getPathForFile(file) method to window.desktopBridge (via preload.ts and the DesktopBridge interface) that synchronously returns the absolute backing path for a DOM File.
  • Introduces composerFileDrop.ts with helpers to partition dropped files into images vs. non-images, normalize paths relative to cwd, and serialize them as composer file-mention tokens.
  • Updates the onComposerDrop handler in ChatComposer to resolve paths via desktopBridge, insert mention tokens for resolved non-image files, and fall back to image attachment for unresolved files. An error toast is shown if the composer is busy when mentions need to be inserted.
  • Behavioral Change: focus is deferred when mentions are inserted to avoid overwriting the insertion snapshot.

Macroscope summarized 254a259.


Note

Medium Risk
Composer drop behavior changes on desktop (mentions vs image errors), and resolved paths are host-local which may not match SSH/WSL workspace paths.

Overview
OS file drops on the chat composer no longer send every file through the image-attachment path. Dropped files are split into images (still attached inline) and everything else, which on desktop can become file mention tokens in the prompt.

A new optional DesktopBridge.getPathForFile (contracts + Electron preload via webUtils.getPathForFile) resolves the on-disk path for each non-image File. composerFileDrop helpers relativize in-workspace paths to match typed/file-tree mentions, serialize links with serializeComposerFileLink, and are covered by unit tests. ChatComposer.onComposerDrop inserts those mentions (with error toast if the composer is busy), avoids eager focus after a successful mention insert, and routes path-unresolved files back through addComposerImages so browser builds and in-memory files keep the prior “unsupported file type” behavior.

Reviewed by Cursor Bugbot for commit 254a259. Bugbot is set up for automated code reviews on this repo. Configure here.

Dropping a file onto the composer previously routed every file through the
image-attachment path, so anything that wasn't an image was rejected with
"Unsupported file type ... attach image files only". There was no way to drop
a file from Finder/Explorer to reference it by path.

Now an OS drop is partitioned: images still attach inline, and other files are
turned into composer file mentions using the dropped File's on-disk path,
resolved via Electron `webUtils.getPathForFile` exposed on the desktop bridge.
Paths inside the workspace cwd are made workspace-relative so they match typed
and file-tree mentions; paths outside stay absolute. Files whose path can't be
resolved (browser builds, in-memory Files) fall back to the existing image
path, so non-desktop behavior is unchanged.

- contracts: add optional DesktopBridge.getPathForFile
- desktop/preload: implement it via webUtils
- web: partitionDroppedComposerFiles / toComposerMentionPath / buildDroppedFileMentions helper + unit tests, wired into ChatComposer onComposerDrop
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b1ad8654-e316-4da6-affc-4471bf893f7f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 5, 2026
Comment on lines +39 to +42
export function toComposerMentionPath(absolutePath: string, cwd: string | null): string {
const normalizedPath = absolutePath.replace(/\\/g, "/");
if (cwd !== null) {
const normalizedCwd = cwd.replace(/\\/g, "/").replace(/\/+$/, "");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium chat/composerFileDrop.ts:39

toComposerMentionPath strips all trailing slashes from cwd, so when cwd is "/" the normalized cwd becomes an empty string and the relativization branch is skipped entirely. Dropping /src/app.ts with the workspace rooted at / returns /src/app.ts instead of the expected src/app.ts. Consider preserving POSIX root "/" as the prefix when normalizing instead of stripping it down to an empty string.

 export function toComposerMentionPath(absolutePath: string, cwd: string | null): string {
   const normalizedPath = absolutePath.replace(/\\/g, "/");
   if (cwd !== null) {
-    const normalizedCwd = cwd.replace(/\\/g, "/").replace(/\/+$/, "");
+    const normalizedCwd =
+      cwd.replace(/\\/g, "/") === "/"
+        ? "/"
+        : cwd.replace(/\\/g, "/").replace(/\/+$/, "");
     if (normalizedCwd.length > 0) {
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/chat/composerFileDrop.ts around lines 39-42:

`toComposerMentionPath` strips all trailing slashes from `cwd`, so when `cwd` is `"/"` the normalized cwd becomes an empty string and the relativization branch is skipped entirely. Dropping `/src/app.ts` with the workspace rooted at `/` returns `/src/app.ts` instead of the expected `src/app.ts`. Consider preserving POSIX root `"/"` as the prefix when normalizing instead of stripping it down to an empty string.

…ions

Review fixes for the drop-to-mention handler:

- The mention token grammar (FILE_LINK_TOKEN_REGEX) requires trailing
  whitespace after a token, and every other insert site appends one; without
  it the last dropped mention stayed plain text and was missed at send time.
- Focusing synchronously after the prompt insert made focusAt push the
  editor's stale pre-mention snapshot back through onChange, overwriting the
  inserted mention -- the exact failure documented on ComposerMentionDropHost.
  The insert path already focuses on the next frame, so only focus here when
  no mention was inserted.
@0xjohnnydev
0xjohnnydev marked this pull request as ready for review August 5, 2026 14:26
@macroscopeapp

macroscopeapp Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new user-facing behavior by enabling non-image file drops to become file mentions in the composer. New features that change runtime behavior warrant human review. Additionally, there is an unresolved comment identifying a bug in path handling for POSIX root directories.

You can customize Macroscope's approvability policy. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant